package com.njtech.pet.ws;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.njtech.pet.constant.Constant;
import com.njtech.pet.dao.FriendDao;
import com.njtech.pet.dao.MessageDao;
import com.njtech.pet.dao.UserDao;
import com.njtech.pet.entity.Friend;
import com.njtech.pet.entity.Message;
import com.njtech.pet.entity.User;
import com.njtech.pet.service.MessageService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ServerEndpoint 注解是一个类层次的注解，它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint("/websocket/{uid}")
public class WebSocketTest {

	static MessageDao messageDao;
	static FriendDao friendDao;
	static UserDao userDao;
	static MessageService messageService;
	/**
	 * 获取Dao或Service一定要这样写
	 */
	public WebSocketTest(){
		ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
		messageDao = (MessageDao) context.getBean("messageDao");
		friendDao = (FriendDao) context.getBean("friendDao");
		userDao = (UserDao)context.getBean("userDao");
		messageService = (MessageService) context.getBean(MessageService.class);
	}

	/**
	 * 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
	 */
	private static int onlineCount = 0;

	/**
	 * concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话，
	 * 可以使用Map来存放，其中Key可以为用户标识
	 */
	private static Map<String,WebSocketTest> connections = new ConcurrentHashMap<>();

	/**
	 * 与某个客户端的连接会话，需要通过它来给客户端发送数据
	 */
	private Session session;

	/**
	 * 用户标识
	 */
	private Integer uid;

	/**
	 * 连接建立成功调用的方法
	 * @param uid  所有得参数
	 * @param session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
	 */
	@OnOpen
	public void onOpen(@PathParam("uid") Integer uid, Session session){
		this.session = session;
		this.uid = uid;

		connections.put(uid.toString(), this);
		System.out.println("现在连接进来的用户id：" + uid);
		//加入set中
		addOnlineCount();
		//在线数加1
		System.out.println("有新连接加入！当前在线人数为" + getOnlineCount());

	}

	/**
	 * 判断当前用户是否已经在ws池中
	 * @param uid
	 * return true:存在
	 */
	public static synchronized boolean checkExit(Integer uid) {
		if (connections.containsKey(uid)) {
				return true;
		}else{
			return true;
		}
	}

	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public void onClose(){
		connections.remove(this.uid);
		//从set中删除
		subOnlineCount();
		//在线数减1
		System.out.println("有一连接关闭！当前在线人数为" + getOnlineCount());
	}

	/**
	 * 收到客户端消息后调用的方法
	 * msg={
	 *     sid:      发消息的用户id
	 *     rid:      待收消息的用户id
	 *     content:  消息内容
     *     type:     （聊天、咨询、售后）（好友请求✔、拒绝请求✔） 客服广播
	 * }
	 * 问题：前台显示的问题
	 * @param message 客户端发送过来的消息
	 * @param session 可选的参数
	 */
	@OnMessage
	public void onMessage(String message, Session session) {
		System.out.println("来自客户端的消息:"+ message+"客户端的id："+session.getId());
		//获得json数据，获取message字段
		JSONObject jsonObject = JSON.parseObject(message);
		User sender = new User();
		User receiver = new User();
		String textMessage = jsonObject.getString("content");
		Integer sid = Integer.valueOf(jsonObject.getString("sid"));
		sender.setId(sid);
		Integer rid = Integer.valueOf(jsonObject.getString("rid"));
		receiver.setId(rid);
		String type = jsonObject.getString("type");

		//情况一：一对一聊天、售后、咨询的操作
		if(type.equals("聊天")||type.equals("售后")||type.equals("咨询")) {
			//1.插入数据库
			Message message1 = new Message(null, null, sender, receiver, textMessage, Constant.Message.STATE_UNREAD.getValue(), type);
			messageDao.insert(message1);
			//找到发送方用户
			User user = userDao.selectByUserId(sid);
			jsonObject.put("username",user.getUsername());
			jsonObject.put("url",user.getHeadUrl());
			//找到这条未读消息的id（待考虑）
			Message thisMessage = messageService.findAMessageByBoth(sid,rid);
			jsonObject.put("messageId",thisMessage.getId());
			jsonObject.put("messageDate",thisMessage.getCreateDate());

			message = jsonObject.toString();
			try {
				//前台接收到数据后，重新调用一次后台selectUnread方法，刷新显示
				//从后台得到json,指明了发送方和接收方，
				// 如果发现当前对话框就是这一对用户的对话框，那么只要调用message一个方法（updateState）,
				// 并把对方的这条消息显示在对话框上。

				//如果现在不在对话框，调用后台的（selectUnread）刷新显示未读消息列表，
				// 一旦用户点击，新建一个对话框（同样存储这一对用户的id）,调用两个方法。
				// 1.调用后台的selectAll,将他们的对话内容都显示出来。
				// 2.调用changeStates方法，把对方给自己的未读消息全部变为已读
				sendMessageTo(message, rid);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		 /******************************************************
		 * A---》后台：{sid: 1, rid: 2, content: "你好！", type: "发送请求"}
		 * 后台---》B：{friendId: 30, rid: 2, type: "发送请求", content: "你好！", sid: 1}
		 * A--》后台：{sid: 1, rid: 2, content: "你好！", type: "拒绝请求"}
		 * 后台--》B：{count: 10, rid: 2, type: "拒绝请求", content: "你好！",sid: 1}
		  * ---------------没有问题
		 */
		if(type.equals("发送请求")||type.equals("拒绝请求")||type.equals("删除")){
			message = jsonObject.toString();
			try {
				//前台接收到数据后，将提示按钮变成红色提醒，点击按扭调用后台findTip
				sendMessageTo(message,rid);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

        /**
         * ###########待考虑！！！！要和陈威那边沟通一下
		 * 用不到了
         */
		if(type.equals("广播")){
			//先根据客服的id(sid)获得客服的所有对应会员
			User user = new User();
			user.setId(sid);
			List<Friend> friends = friendDao.selectByUser(user,Constant.Friend.STATE_ACCEPTED.getValue(),null,null,"friendName asc");
			//存储关联会员的id
			List<Integer>list = new ArrayList<>();
			for (Friend temp : friends) {
				list.add(temp.getFriend().getId());
			}
			for(int i=0;i<list.size();i++) {
				//插入多条数据
				Message message1 = new Message(null,null,sender,receiver,textMessage, Constant.Message.STATE_UNREAD.getValue(), "咨询");
				messageDao.insert(message1);
			}
			broadcast(jsonObject,list);
		}
	}

	/**
	 * 发生错误时调用
	 * @param session 连接的session
	 * @param error  错误
	 */
	@OnError
	public void onError(Session session, Throwable error){
		System.out.println("发生错误");
		connections.remove(this.uid);
		error.printStackTrace();
	}

	/*******************************
	 * 把消息发送给指定用户-----完成
	 * @param message  消息
	 * @throws IOException 异常
	 */
	public void sendMessageTo(String message,Integer uid) throws IOException{
		for (WebSocketTest item : connections.values()) {
			if (item.uid.equals(uid) ) {
				item.session.getAsyncRemote().sendText(message);
				break;
			}
		}
	}

	/**
	 * 广播消息，主要针对私人定制客服，一对多的关系，可能会有一些公共消息
	 * 这个广播消息是否要插入消息的表中：1条或多条？多条
	 * 前台在用户刚上线的时候如何显示？？1条：先找好友关系表中的该用户对应的客服，
	 * 						查询消息表中该客服发送的未读消息？？行不通
	 * 			多条：找消息表中客服---》A的未读消息，不用群聊，就是一对一，可以实现
	 * @param jsonObject
	 */
	public void broadcast(JSONObject jsonObject,List<Integer> list){
		for (WebSocketTest item : connections.values()) {
			//对每一个在线的用户，看他是否为客服的好友，如果是，发送消息
			if (list.contains(item.uid)) {
				String message=jsonObject.toString();
				item.session.getAsyncRemote().sendText(message);
			}
		}

	}

	public static synchronized int getOnlineCount() {
		return onlineCount;
	}

	public static synchronized void addOnlineCount() {
		WebSocketTest.onlineCount++;
	}

	public static synchronized void subOnlineCount() {
		WebSocketTest.onlineCount--;
	}

}